use std::{collections::HashMap, io::Read, sync::LazyLock};

use http_body_util::{combinators::BoxBody, BodyExt, Full, StreamBody};
use serde::de::DeserializeOwned;
use wtask_app::{StreamExt, WStreamReader};
use wtask_base::{encrypt::{decrypt_str_to_str, encrypt_str_to_str}, error::WResult, export::{TokioRwLock, serde_from_str}, machine::CMDArgsConfigMachine};
use wtask_file::{BufBody, BytesBody, FrameBody, HyperError, IncomingBody, Request, Response, StatusCode, header};

use crate::handle::{flow::storage::FlowStorage, login::LoginStorage, port::PortStorage, room::RoomStorage};


// static STREAM_THRESHOLD: usize = 1024 * 1024 * 8;
static INTERNAL_SERVER_ERROR: &str = "Internal Server Error";
static NOTFOUND: &str = "Not Found";


pub(crate) type MachineConfigs = HashMap<String, CMDArgsConfigMachine>;


pub struct WebSession {
    pub(crate) room: TokioRwLock<RoomStorage>,
    pub(crate) login: TokioRwLock<LoginStorage>,
    pub(crate) port: TokioRwLock<HashMap<u32, PortStorage>>,
    pub(crate) flow: TokioRwLock<FlowStorage>,
}

impl WebSession {
    pub async fn drop_data(&self) {
        self.room.write().await.drop_data();
        self.login.write().await.drop_data();
        self.port.write().await.clear();
        self.flow.write().await.drop_data();
    }
}

pub static WEB_SESSION: LazyLock<WebSession> = LazyLock::new(|| {
    WebSession {
        room: TokioRwLock::new(RoomStorage::default()),
        login: TokioRwLock::new(LoginStorage::default()),
        port: TokioRwLock::new(HashMap::new()),
        flow: TokioRwLock::new(FlowStorage::default()),
    }
});


pub(crate) type ResponseBody = BoxBody<BytesBody, HyperError>;



pub(crate) fn response_body(data: &str, key: &str) -> ResponseBody {
    let data_en = encrypt_str_to_str(data, key).unwrap();
    Full::new(data_en.into())
        .map_err(|never| match never {})
        .boxed()
}


pub(crate) fn response_body_normal<T: Into<BytesBody>>(data: T) -> ResponseBody {
    Full::new(data.into())
        .map_err(|never| match never {})
        .boxed()
}



pub(crate) fn not_found() -> Response<ResponseBody> {
    Response::builder()
        .status(StatusCode::NOT_FOUND)
        .body(response_body_normal(NOTFOUND))
        .unwrap()
}

pub(crate) fn internal_error() -> Response<ResponseBody> {
    Response::builder()
        .status(StatusCode::INTERNAL_SERVER_ERROR)
        .body(response_body_normal(INTERNAL_SERVER_ERROR))
        .unwrap()
}


fn response_file_basic(boxed_body: BoxBody<BytesBody, HyperError>, is_gzip: bool, content_type: &str) -> WResult<Response<ResponseBody>> {
    let mut response = Response::builder()
        .status(StatusCode::OK)
        .header(header::CONTENT_TYPE, content_type);
    if is_gzip {
        response = response
            .header(header::CONTENT_ENCODING, "gzip")
            .header(header::CACHE_CONTROL, "max-age=8640000");
    }
    let response = response.body(boxed_body).unwrap();
    Ok(response)
}


pub(crate) fn response_file_data(data: Vec<u8>, is_gzip: bool, content_type: &str) -> WResult<Response<ResponseBody>> {
    let boxed_body = Full::from(BytesBody::from(data))
            .map_err(|e| match e {})
            .boxed();
    response_file_basic(boxed_body, is_gzip, content_type)
}



pub(crate) fn response_file_stream(stream_reader: WStreamReader, is_gzip: bool, content_type: &str) -> WResult<Response<ResponseBody>> {
    let stream = stream_reader.map(|b| Ok::<FrameBody<BytesBody>, HyperError>(FrameBody::data(b.into())));
    let boxed_body = BodyExt::boxed(StreamBody::new(stream));
    response_file_basic(boxed_body, is_gzip, content_type)
}



pub(crate) fn parse_reader<T: Read>(mut reader: T, token: &str) -> WResult<String> {
    let mut body = String::new();
    reader.read_to_string(&mut body)?;
    let body_de = decrypt_str_to_str(&body, token)?;
    Ok(body_de)
}



pub(crate) async fn parse_body_string(req: Request<IncomingBody>, token: &str) -> WResult<String> {
    let whole_body = req.collect().await.unwrap().aggregate().reader();
    parse_reader(whole_body, token)
}


pub(crate) async fn parse_body<T: DeserializeOwned>(req: Request<IncomingBody>, token: &str) -> WResult<T> {
    let whole_body = req.collect().await.unwrap().aggregate().reader();
    let body_de = parse_reader(whole_body, token)?;
    let res = serde_from_str::<T>(&body_de)?;
    Ok(res)
}